package service

import (
	"context"
	"devops/controller/validation"
	"devops/models"
	"devops/pkg/git/git_model"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/google/uuid"
)

type toJsonResponse struct {
	Status string                 `json:"status"`
	Data   map[string]interface{} `json:"data"`
}

func (d *devOps) ToJson(jenkinsfile string) (map[string]interface{}, error) {
	ctx := context.Background()

	response := toJsonResponse{}
	data := map[string]string{
		"jenkinsfile": jenkinsfile,
	}
	_, err := d.jks.Requester.Post(ctx, "/pipeline-model-converter/toJson", nil, &response, data)
	if err != nil {
		return nil, err
	}
	return response.Data, nil
}

func (d *devOps) ToJenkinsfile(jksfileJson string) (map[string]interface{}, error) {
	qr := map[string]string{"json": jksfileJson}
	jenkinsfile := toJsonResponse{}
	resp, err := d.jks.Requester.Post(context.Background(), "/pipeline-model-converter/toJenkinsfile", nil, &jenkinsfile, qr)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, errors.New("error code :" + resp.Status)
	}

	return jenkinsfile.Data, nil
}

func (d *devOps) ValidateJson(jksfileJson string) (map[string]interface{}, error) {
	client := &http.Client{}
	uri := fmt.Sprintf("%s/pipeline-model-converter/validateJson", d.jks.Server)
	req, err := http.NewRequest("POST", uri, strings.NewReader("json="+jksfileJson))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	crumbData := map[string]string{}
	response, _ := d.jks.Requester.GetJSON(context.Background(), "/crumbIssuer/api/json", &crumbData, nil)
	if response.StatusCode == 200 && crumbData["crumbRequestField"] != "" {
		req.SetBasicAuth(d.jks.Requester.BasicAuth.Username, d.jks.Requester.BasicAuth.Password)
		req.Header.Set(crumbData["crumbRequestField"], crumbData["crumb"])
		req.Header.Set("Cookie", response.Header.Get("set-cookie"))

	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	b, _ := ioutil.ReadAll(resp.Body)
	jenkinsfile := toJsonResponse{}
	json.Unmarshal(b, &jenkinsfile)
	return jenkinsfile.Data, nil
}

func (d *devOps) CreateCI(ci validation.CreateCI) (*models.CI, error) {
	project := models.GetProjectByID(ci.ProjectId)
	if project.ID == 0 {
		return nil, errors.New("项目不存在")
	}

	// 记录数据
	ciModel := models.CI{
		ProjectId:          ci.ProjectId,
		Name:               ci.Name,
		ScmType:            ci.ScmType,
		RepositoryUrl:      ci.RepositoryUrl,
		RepositoryUsername: ci.RepositoryUsername,
		RepositoryToken:    ci.RepositoryToken,
		Jenkinsfile:        ci.Jenkinsfile,
		JenkinsfileType:    ci.JenkinsfileType,
		Branch:             ci.Branch,
		CredentialsId:      uuid.NewString(),
		Repository:         ci.Repository,
		RepositoryOwner:    ci.RepositoryOwner,
		RepositoryPath:     ci.RepositoryPath,
		TemplateId:         ci.TemplateID,
		BaseURL:            ci.BaseURL,
	}
	// mysql 创建 ci 信息
	if err := models.CreateCI(&ciModel); err != nil {
		return nil, errors.New("Create CI Error: " + err.Error())
	}

	// 如果有制品库相关，同步创建内置环境变量
	if ci.DockerRepoID != 0 {
		if err := d.CreateDockerEnv(ciModel.ID, ci.DockerRepoID, ci.ImageName, ci.ImageVersion, ci.DockerfilePath); err != nil {
			// 回滚已经创建的数据
			d.CreateCIRollBack(ciModel, project.Name)
			return nil, errors.New("Create Env Error: " + err.Error())
		}
	}
	// 在 Jenkins 创建基本的条目
	if err := d.CreateItem(ci.Name, project.Name); err != nil {
		d.CreateCIRollBack(ciModel, project.Name)
		return nil, errors.New("Jenkins create Item err: " + err.Error())
	}
	var err error
	// 给 仓库创建对应的 凭证
	// 第三方GIT仓库创建token凭证
	// 自建 GIT 服务使用账号密码凭证
	if ci.ScmType == "git" {
		err = d.CreateUserPwdSecret(project.Name, ci.RepositoryUsername, ci.RepositoryToken, ciModel.CredentialsId)
	} else {
		err = d.CreateSecret(project.Name, ci.RepositoryToken, ciModel.CredentialsId)
	}
	if err != nil {
		d.CreateCIRollBack(ciModel, project.Name)
		return nil, err
	}
	// 在 Jks 创建完整的信息
	if err := d.ConfigSubmit(ciModel, project.Name, SecretUuid(ciModel.CredentialsId)); err != nil {
		d.CreateCIRollBack(ciModel, project.Name)
		return nil, err
	}
	return &ciModel, nil
}

func (d *devOps) CreateCIRollBack(ci models.CI, projectName string) error {
	var err error
	// 删除创建好的东西
	err = models.DeleteCI(ci.ID)
	err = models.DeleteCiEnvByCid(ci.ID)

	_, err = d.jks.DeleteJob(context.Background(), projectName+"/job/"+ci.Name)
	err = d.DeleteSecret(projectName, ci.CredentialsId)
	return err
}

func (d *devOps) UpdateCI(update validation.UpdateCI) (*models.GetCIOneResp, error) {
	ci := models.GetCIByID(update.CID)
	if ci == nil {
		return nil, errors.New("构建计划不存在")
	}
	project := models.GetProjectByID(ci.ProjectId)
	if project.ID == 0 {
		return nil, errors.New("项目不存在")
	}

	// 记录数据
	ciModel := models.CI{
		Name:               ci.Name,
		ProjectId:          ci.ProjectId,
		ScmType:            update.ScmType,
		RepositoryUrl:      update.RepositoryUrl,
		RepositoryUsername: update.RepositoryUsername,
		RepositoryToken:    update.RepositoryToken,
		JenkinsfileType:    update.JenkinsfileType,
		Branch:             update.Branch,
		Repository:         update.Repository,
		RepositoryOwner:    update.RepositoryOwner,
		RepositoryPath:     update.RepositoryPath,
		CredentialsId:      uuid.NewString(),
	}
	var err error
	if update.ScmType == "git" {
		err = d.CreateUserPwdSecret(project.Name, ci.RepositoryUsername, ci.RepositoryToken, ciModel.CredentialsId)
	} else {
		err = d.CreateSecret(project.Name, ci.RepositoryToken, ciModel.CredentialsId)
	}
	if err != nil {
		return nil, err
	} else {
		// 创建新的 secret 成功 删除老的 secret
		d.DeleteSecret(project.Name, ci.CredentialsId)
	}
	models.UpdateCI(update.CID, ciModel)
	ciResp := d.GetOneCI(update.CID)
	if err := d.ConfigSubmit(ciResp.CI, project.Name, SecretUuid(ciModel.CredentialsId)); err != nil {
		return nil, err
	}
	return ciResp, nil
}

func (d *devOps) UpdateJksfile(cid int, file string) (*models.CI, error) {
	ci := models.GetCIByID(cid)
	if ci == nil {
		return nil, errors.New("构建计划不存在")
	}
	project := models.GetProjectByID(ci.ProjectId)
	if project.ID == 0 {
		return nil, errors.New("项目不存在")
	}

	err := d.ConfigSubmit(*ci, project.Name, Jenkinsfile(file))
	if err != nil {
		return nil, err
	}
	models.UpdateCI(cid, models.CI{
		Jenkinsfile: file,
	})
	return models.GetCIByID(cid), err
}

func (d *devOps) UpdateCiName(cid int, name string) (*models.CI, error) {
	ci := models.GetCIByID(cid)
	if ci == nil {
		return nil, errors.New("构建计划不存在")
	}
	project := models.GetProjectByID(ci.ProjectId)
	if project.ID == 0 {
		return nil, errors.New("项目不存在")
	}

	// 检查是否重名
	if models.GetCIByPIDAndName(project.ID, name) != nil {
		return nil, errors.New("计划名已存在")
	}

	err := d.RenameJob(ci.Name, project.Name, name)
	if err != nil {
		return nil, err
	}
	models.UpdateCI(cid, models.CI{
		Name: name,
	})
	return models.GetCIByID(cid), nil
}

func (d *devOps) DeleteCI(projectID int, cid int) error {
	project := models.GetProjectByID(projectID)
	if project.ID != projectID {
		return errors.New("项目不存在")
	}
	ci := models.GetCIByID(cid)
	if ci == nil {
		return errors.New("构建计划不存在")
	}
	//// 和 jenkins server 交互
	//ctx := context.Background()
	//_, err := d.jks.DeleteJob(ctx, project.Name+"/job/"+ci.Name)
	//if err != nil && err.Error() != "404"{
	//	return err
	//}
	//
	//d.DeleteSecret(project.Name, ci.CredentialsId)
	//// 更新数据
	//models.DeleteCI(cid)
	return d.CreateCIRollBack(*ci, project.Name)
}

func (d *devOps) GetProjectCI(projectID int, page int, pageSize int, search string) ([]*models.ProjectCI, int) {
	var (
		cis   []*models.CI
		total int
	)
	if search != "" {
		cis, total = models.GetProjectCIPageSearch(projectID, page, pageSize, search)
	} else {
		cis, total = models.GetProjectCIPage(projectID, page, pageSize)
	}
	project := models.GetProjectByID(projectID)
	projectCIs := make([]*models.ProjectCI, 0, len(cis))
	for _, ci := range cis {
		var buildAndStages *models.BuildAndStages
		build := models.GetCILastBuild(ci.ID)
		if build != nil {
			stages := models.GetStageByBuildIDAndFlows(build.ID)

			// 计划列表新增 docker制品 和 部署组信息
			var image, deployGroupID, deployGroupName string
			if env := models.GetEnvByCIDName(build.CID, "DOCKER_IMAGE_NAME"); env != nil {
				image = fmt.Sprintf("%s:%s", env.Default, build.ImageTag)
			}
			if cd := models.GetCdByBuildID(build.ID); cd.GroupId != "" {
				if group, err := d.GetGroupDetail(cd.GroupId); err == nil && group != nil {
					deployGroupID = cd.GroupId
					deployGroupName = *group.Response.Result.GroupName
				}
			}

			buildAndStages = &models.BuildAndStages{build, stages, deployGroupID, deployGroupName, image}
		}
		go d.SyncBuildResult(ci.Name, project.Name, build)
		projectCIs = append(projectCIs, &models.ProjectCI{ci, buildAndStages})
	}
	return projectCIs, total
}

func (d *devOps) GetOneCI(cid int) *models.GetCIOneResp {
	ci := models.GetCIByID(cid)
	if ci == nil {
		return nil
	}
	branches := make([]git_model.Branch, 0)
	repos := make([]git_model.Repo, 0)
	//if ci.ScmType == "github" || ci.ScmType == "gitee" {
	//	cli := git.NewGitCli(ci.ScmType, ci.RepositoryToken, "")
	//	branches = cli.GetBranches(ci.RepositoryOwner, ci.RepositoryPath)
	//	repos, _ = cli.GetRepos()
	//}
	return &models.GetCIOneResp{*ci, branches, repos, models.GetTemplateByID(ci.TemplateId)}
}

func (d *devOps) GetOneCIBaseInfo(cid int) *models.CI {
	return models.GetCIByID(cid)
}
